﻿#pragma once

#include <unordered_map>
#include <functional>
#include <memory>
#include <list>
#include <ctime>
#include <vector>
#include "console.hh"
#include "../util/box_std_allocator.hh"

namespace kratos {
namespace service {
class ServiceBox;
class CommandImpl;
class ServiceHttpLoader;
}
}

namespace Json {
class Value;
}

namespace kratos {
namespace console {

/**
 * 基于页面的Console 
 */
class ConsoleImpl : public Console {
  using APIClassMethod  = std::string(ConsoleImpl::*)(const std::string&);
  using OnOffMethod     = bool (ConsoleImpl::*)(bool, std::string&);
  using AttributeMethod = bool (ConsoleImpl::*)(const std::string&,
      const std::string&, std::string&);
  /**
   * 打点数据 
   */
  struct Spot {
      std::string date; ///< 日期
      std::size_t size; ///< 度量值
  };
  using History = std::list<
      Spot,
      service::Allocator<Spot>
  >;
  using CommandMap = std::unordered_map<
    std::string,
    std::unique_ptr<service::CommandImpl>,
    std::hash<std::string>,
    std::equal_to<std::string>,
    service::Allocator<
      std::pair<
        const std::string,
        std::unique_ptr<service::CommandImpl>
      > // std::pair
    > // service::Allocator
  >; // std::unordered_map

  using OnOffMethodMap = std::unordered_map<
    std::string,
    std::function<bool(bool, std::string&)>,
    std::hash<std::string>,
    std::equal_to<std::string>,
    service::Allocator<
      std::pair<
        const std::string,
        std::function<bool(bool, std::string&)>
      > // std::pair
    > // service::Allocator
  >; // std::unordered_map

  using AttributeMethodMap = std::unordered_map<
    std::string,
    std::function<bool(const std::string&, const std::string&, std::string&)>,
    std::hash<std::string>,
    std::equal_to<std::string>,
    service::Allocator<
      std::pair<
        const std::string,
        std::function<bool(const std::string&, const std::string&, std::string&)>
      > // std::pair
    > // service::Allocator
  >; // std::unordered_map

  using ConsoleSwitchPluginMap = std::unordered_map<
    std::string,
    ConsoleSwitchPlugin,
    std::hash<std::string>,
    std::equal_to<std::string>,
    service::Allocator<
      std::pair<
        const std::string,
        ConsoleSwitchPlugin
      > // std::pair
    > // service::Allocator
  >; // std::unordered_map

  using ConsoleSelectionPluginMap = std::unordered_map<
    std::string,
    ConsoleSelectionPlugin,
    std::hash<std::string>,
    std::equal_to<std::string>,
    service::Allocator<
      std::pair<
        const std::string,
        ConsoleSelectionPlugin
      > // std::pair
    > // service::Allocator
  >; // std::unordered_map

  service::ServiceBox* box_{ nullptr };           ///< 服务容器
  OnOffMethodMap       on_off_method_map_;        ///< 开关API表
  AttributeMethodMap   attr_method_map_;          ///< 选项API表
  CommandMap           command_map_;              ///< 页面
  std::string          root_path_html_file_path_; ///< 主页面的文件路径
  History              memory_history_;           ///< 内存打点
  History              service_history_;          ///< RPC调用打点
  std::size_t          last_total_call_count_{0}; ///< 最新的总RPC调用次数
  std::time_t          last_service_tick_{0};     ///< 上一次打点的时间戳，秒

  ConsoleSwitchPluginMap    switch_plugin_map_;    ///< 开关plugin
  ConsoleSelectionPluginMap selection_plugin_map_; ///< 选项plugin

  std::size_t service_reload_count_{0}; ///< 需要reload的服务数量
  std::uint64_t current_user_data_{0}; ///< 当前调用的用户数据

public:
  /**
   * 构造
   *
   * \param box 服务容器
   */
   ConsoleImpl(service::ServiceBox* box);
  /**
   * 析构 
   */
  virtual ~ConsoleImpl();
  virtual auto add_switch(const std::string& name,
      ConsoleSwitchPlugin switch_plugin)->void override;
  virtual auto remove_switch(const std::string& name)->void override;
  virtual auto add_selection(const std::string& name,
      ConsoleSelectionPlugin selection_plugin)->void override;
  virtual auto remove_selection(const std::string& name)->void override;
  virtual auto get_current_user_data() -> std::uint64_t override;
  /**
   * 启动
   *
   * \return true或false
   */
  auto start()->bool;
  /**
   * 关闭
   *
   * \return true或false
   */
  auto stop()->bool;
  /**
   * 主循环  
   */
  auto update()->void;

private:
  /**
   * 加载配置
   * 
   * \return true或false
   */
  auto load_config() -> bool;
  /**
   * 注册路径API
   *
   * \param path HTTP调用路径
   * \param method API
   */
  auto register_api(const std::string& path, APIClassMethod method)->void;
  /**
   * 注册开关API
   *
   * \param switch_name 开关名
   * \param method API
   */
  auto register_on_off_method(const std::string& switch_name,
      OnOffMethod method)->void;
  /**
   * 注册选项API
   *
   * \param attr_name 属性名
   * \param method API
   */
  auto register_selection_method(const std::string& attr_name,
      AttributeMethod method)->void;
  /**
   * 根页面
   */
  auto root_command(const std::string&)->std::string;
  /**
   * 解析JSON
   * \param [OUT] root 根节点
   * \param json_string JSON串
   * \param [OUT]error 错误
   */
  auto parse_json(Json::Value& root, const std::string& json_string,
      std::string& error)->bool;
  /**
   * 解析JSON并取出一个属性值
   * \param json_string JSON串
   * \param name 属性名
   * \param [OUT]value 取出的属性值
   */
  auto parse_json_and_get_attribute(const std::string& json_string,
      const std::string& name, std::string& value)->bool;

private:
  //
  // 以下为路径API
  //
  auto basic_command(const std::string&)->std::string;
  auto basic_memory(const std::string&)->std::string;
  auto detail_memory(const std::string&)->std::string;
  auto memory_history(const std::string&)->std::string;
  auto registered_service(const std::string&)->std::string;
  auto reference_service(const std::string&)->std::string;
  auto loaded_service(const std::string&)->std::string;
  auto service_call(const std::string&)->std::string;
  auto detail_service_call(const std::string&)->std::string;
  auto service_history(const std::string&)->std::string;
  auto all_switch(const std::string&)->std::string;
  auto switch_on_off(const std::string& switch_json)->std::string;
  auto all_selection(const std::string&)->std::string;
  auto selection_change(const std::string& new_attr)->std::string;
  auto current_version(const std::string&)->std::string;
  auto current_version_api(const std::string&)->std::string;
  auto install(const std::string& json_str)->std::string;

private:
  /**
   * 解析开关API的JSON请求
   *
   * \param switch_json JSON请求
   * \param [OUT] root 返回的JSON根对象
   * \return true或false
   */
  auto parse_switch_on_off_json(const std::string& switch_json,
      Json::Value& root) -> bool;
  /**
   * 添加开关
   * 
   * \param [OUT] ret 返回给客户端的JSON
   * \param switch_name 开关名
   * \param display_name 客户端显示的开关名
   * \param on_off 当前状态
   * \param tips 客户端弹出的提示信息
   */
  auto add_switch(
      Json::Value& ret,
      const std::string& switch_name,
      const std::string& display_name,
      bool on_off,
      const std::string& tips = "") -> void;
  //
  // 以下为开关API
  //
  auto on_switch_is_open_system_exception(bool, std::string&) -> bool;
  auto on_switch_is_dump_heap(bool, std::string&) -> bool;
  auto on_switch_is_open_remote_update(bool, std::string&) -> bool;
  auto on_switch_is_open_rpc_stat(bool, std::string&) -> bool;
  auto on_switch_is_gc_on_off(bool, std::string&) -> bool;

  /**
   * 尝试调用plugin处理开关 
   */
  auto try_call_plugin_switch(const std::string& name, bool on_off, std::string& result)->bool;

private:
  /**
   * 解析选项API的JSON请求
   *
   * \param new_attr_json 选项的JSON请求
   * \param [OUT] root 返回的JSON根对象
   * \return true或false
   */
  auto parse_attr_json(const std::string& new_attr_json,
      Json::Value& root) -> bool;
  /**
   * 添加选项
   * 
   * \param ret 返回给客户端的JSON
   * \param attr_name 属性名
   * \param display_name 客户端显示的选项名
   * \param selections 选项
   * \param tips 客户端弹出的提示信息
   */
  auto add_selection(
      Json::Value& ret,
      const std::string& attr_name,
      const std::string& display_name,
      const std::vector<std::string>& selections,
      const std::string& tips = "") -> void;

  //
  // 以下为选项API
  //
  auto on_select_max_frame(
      const std::string&,
      const std::string&,
      std::string&) -> bool;
  auto on_select_service_finder_connect_timeout(
      const std::string&,
      const std::string&,
      std::string&) -> bool;
  auto on_select_connect_other_box_timeout(
      const std::string&,
      const std::string&,
      std::string&) -> bool;
  auto on_select_box_channel_recv_buffer_len(
      const std::string&,
      const std::string&,
      std::string&) -> bool;
  auto on_select_http_max_call_timeout(const std::string&,
      const std::string&,
      std::string&) -> bool;
  auto on_select_remote_repo_check_interval(const std::string&,
      const std::string&,
      std::string&) -> bool;
  auto on_select_gc_intval(const std::string&,
      const std::string&,
      std::string&) -> bool;
  /**
   * 尝试调用plugin处理选项
   */
  auto try_call_selection_plugin(const std::string& name,
      const std::string& value,
      std::string& result)->bool;
};

}
}
